#include "BuildingBar.h"

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>


#include <stdio.h>
#include <windows.h>
#include <process.h>         // needed for _beginthread()

#include "OptRun.h"
#include "galibFuncs.h"

using namespace OptRun;
using namespace std;

BuildingBar::BuildingBar(std::string path)
  : bar_(new BuildingBarInfor)
{
  bar_->working_dir_ = path;

  char databasefile[100];
  sprintf(databasefile,"%s%s", bar_->working_dir_.c_str(), "/BuildingBar_Log.txt");
  ofstream logFile;
  logFile.open(databasefile, std::ios_base::app);
  if( logFile.fail() ){
    cerr << "Cannot open " << databasefile << " for input.\n";
    //exit(1);
    return;
  }

  logFile << "-------------------------------------------------\n";
  time_t t;
  struct tm *timeinfo;
  time (&t);
  timeinfo = localtime (&t);
  logFile << "construct BuildingBar: " << this  << "   at: " << asctime(timeinfo) << "\n";
  logFile.close();
}


BuildingBar::~BuildingBar(void)
{
  char databasefile[100];
  sprintf(databasefile,"%s%s", bar_->working_dir_.c_str(), "/BuildingBar_Log.txt");
  ofstream logFile;
  logFile.open(databasefile, std::ios_base::app);
  if( logFile.fail() ){
    cerr << "Cannot open " << databasefile << " for input.\n";
    //exit(1);
    return;
  }

  time_t t;
  struct tm *timeinfo;
  time (&t);
  timeinfo = localtime (&t);
  logFile << "~ BuildingBar: " << this  << "   at: " << asctime(timeinfo) << "\n";
  logFile.close();
}

const char* BuildingBar::Name() const
{
  //std::string name = "Standard Foot Print: ";
  std::string name = "Building Bar Bar: ";
  char buf[16];
  itoa(bar_->length_, buf, 10);
  name.append(buf).append(" x ");
  itoa(bar_->width_, buf, 10);
  name.append(buf);
  name.append(" : [");
  for (int i=0; i<bar_->possible_spans_.size(); i++)
  {
    itoa(bar_->possible_spans_[i], buf, 10);
    name.append(buf).append(", ");
  }
  name.append("]");
  return name.c_str();
}


void optimizer(void *wPtr_bar);

void BuildingBar::RunGenerator()
{
  bar_->isOptFinished_ = false;
  ofstream alogfile;
  char logstring[100];
  sprintf(logstring,"%s", "C:\\sketchup_log\\log_sketchup_plugin.txt");
  alogfile.open(logstring);
  alogfile << " database_path_.c_str():  "<< (bar_->working_dir_ + "/DataBase.txt").c_str() << "\n";
  alogfile << "(char*)((void*): "<< (char*)((void*)(bar_->working_dir_ + "/DataBase.txt").c_str()) << "\n";
  alogfile.close();
  //_beginthread( optimizer, 0, (void*)working_dir_.c_str() );
  //std::weak_ptr<BuildingBarInfor> wPtr_bar=bar_;
  //_beginthread( optimizer, 0, &wPtr_bar );
  _beginthread( optimizer, 0, bar_ );
}

const char* BuildingBar::CurrentDirectory()
{

  ofstream myfile;
  myfile.open("ga_log.txt");

  myfile.close();

  return bar_->database_path_.c_str();
}



void optimizer(void * wPtr_bar)//, void *arg_working_dir)
{
  ofstream alogfile;
  char logstring[100];
  //sprintf(logstring,"%s%s", arg_working_dir, "/sketchup_ga_log.txt");
  sprintf(logstring,"%s", "C:\\sketchup_log\\log_sketchup_plugin_opt.txt");
  alogfile.open(logstring);
  //alogfile << (char*)(arg_working_dir) << "\n";
  alogfile << "start \n";
  alogfile.close();

  //shared_ptr<BuildingBarInfor> sPtr_bar((BuildingBarInfor*)wPtr_bar);
  BuildingBarInfor* sPtr_bar = static_cast<BuildingBarInfor*>(wPtr_bar);
  //shared_ptr<BuildingBarInfor> sPtr_bar = static_cast<std::weak_ptr<BuildingBarInfor>*>(wPtr_bar)->lock();
  if ( sPtr_bar == nullptr)
  {
    return;
  }

  alogfile.open(logstring, std::ios_base::app);
  //alogfile << (char*)(arg_working_dir) << "\n";
  alogfile << "wPtr_bar : " <<wPtr_bar<< "\n";
  alogfile.close();
  alogfile.open(logstring, std::ios_base::app);
  //alogfile << (char*)(arg_working_dir) << "\n";
  alogfile << "sPtr_bar : " <<sPtr_bar<< "\n";
  alogfile.close();

  alogfile.open(logstring, std::ios_base::app);
  //alogfile << (char*)(arg_working_dir) << "\n";
  alogfile << sPtr_bar->working_dir_ << "\n";
  alogfile.close();



  alogfile.open(logstring, std::ios_base::app);

  int i,j;
  clock_t start,finish;
  double totaltime;

  // See if we've been given a seed to use (for testing purposes).  When you
  // specify a random seed, the evolution will be exactly the same each time
  // you use that seed number.

  start = clock();
  //for(i=1; i<argc; i++) {
  //  if(strcmp(argv[i++],"seed") == 0)
  GARandomSeed((unsigned int)start);
  //}



  char databasefile[100];
  //sprintf(databasefile,"%s%s", (char *)arg_working_dir, "/DataBase.txt");
  sprintf(databasefile,"%s%s", sPtr_bar->working_dir_.c_str(), "/DataBase.txt");

  //const char filename3[128] = database_path_.c_str();//"DataBase.txt";
  //char filename3[128] = "..\\DataBase.txt";
  ifstream inStream(databasefile);
  if(!inStream){
    cerr << "Cannot open " << databasefile << " for input.\n";
    alogfile << "Cannot open " << databasefile << " for input.\n";
    //exit(1);
    return;
  }

  alogfile.close();

  vector<vector<double>> & Holorib = OptInf::Holorib;
  vector<vector<double>> & Cofrasta = OptInf::Cofrasta;
  vector<vector<double>> & CrossSection = OptInf::CrossSection;
  vector<vector<double>> & FullLoad = OptInf::FullLoad;
  vector<vector<double>> & LeftLoad = OptInf::LeftLoad;
  vector<vector<double>> & RightLoad = OptInf::RightLoad;
  vector<vector<double>> & SlabProfile = OptInf::SlabProfile;
  vector<vector<double>> & Five_Span_Load = OptInf::Five_Span_Load;

  vector<std::string> & CrossSectionName = OptInf::CrossSectionName;

  for(int i=0; i<5; i++)
  {
    inStream >> OptInf::ai[i];
  }


  inStream >> OptInf::PopulationSize;
  inStream >> OptInf::GenerationSize;
  inStream >> OptInf::MutationProbability;
  inStream >> OptInf::CrossoverProbability;


  inStream >> OptInf::LENGTH; // the length of the floor 50m
  inStream >> OptInf::WIDTH; // the width of the floor 15m
  inStream >> OptInf::H_MAX; // maximum construction height 600mm
  inStream >> OptInf::X_COL; // the start range of the column position 4.3m.x_col must >= 4.3m
  inStream >> OptInf::Y_COL; // the end range of the column position 5m. y_col must <= 7.5m
  inStream >> OptInf::Dist_COL; //the distance between the columns 0.3m


  OptInf::N_of_Floor = 8;

  OptInf::LENGTH = (float)(sPtr_bar->length_)/1000.0f;
  OptInf::WIDTH = (float)(sPtr_bar->width_)/1000.0f;
  OptInf::H_MAX = (float)(sPtr_bar->construt_height_)/1000.0f;
  OptInf::N_of_Floor = sPtr_bar->no_of_stories_;
  OptInf::NO_OF_GENE = 11 + sPtr_bar->no_of_stories_*4;

  for(int i=0; i<OptInf::height1; i++)
  {
    for(int j=0; j<OptInf::width1; j++)
      inStream >> OptInf::CrossSection[i][j];       // read in profile table

    inStream >> OptInf::CrossSectionName[i];
  }

  for(i=0; i<OptInf::height2; i++)
    for(j=0; j<OptInf::width2; j++)
      inStream >> Holorib[i][j]; 

  for(i=0; i<OptInf::height2; i++)
    for(j=0; j<OptInf::width2; j++)
      inStream >> Cofrasta[i][j];
  for(i=0; i<OptInf::height3; i++)
    for(j=0; j<OptInf::width3; j++)
      inStream >> FullLoad[i][j];
  for(i=0; i<OptInf::height3; i++)
    for(j=0; j<OptInf::width3; j++)
      inStream >> LeftLoad[i][j];
  for(i=0; i<OptInf::height3; i++)
    for(j=0; j<OptInf::width3; j++)
      inStream >> RightLoad[i][j];
  for(i=0; i<OptInf::height4; i++)
    for(j=0; j<OptInf::width4; j++)
      inStream >> SlabProfile[i][j];
  for(i=0; i<OptInf::height5; i++)
    for(j=0; j<OptInf::width5; j++)
      inStream >> Five_Span_Load[i][j];

  inStream.close();

  char myfilename[100];
  //sprintf(myfilename,"%s%s", (char *)arg_working_dir, "/ga_log.txt");
  sprintf(myfilename,"%s%s", sPtr_bar->working_dir_.c_str(), "/ga_log.txt");
  ofstream myfile;
  myfile.open(myfilename);


  char resultfilename[100];
  //sprintf(resultfilename,"%s%s", (char *)arg_working_dir, "/ga_result.txt");
  sprintf(resultfilename,"%s%s", sPtr_bar->working_dir_.c_str(), "/ga_result.txt");

  ofstream resultfile;
  resultfile.open(resultfilename);
  resultfile.close();
  //UserData data;  //only for schema gainga
  //data.geo_genome = new GA1DArrayGenome<int>(OptInf::NO_OF_GENE,GaInGa_GeoObjective,(void *)&data);//only for schema gainga
  //   data.pro_genome = new GA1DArrayGenome<int>(OptInf::NO_OF_ProGENE,GaInGa_ProObjective,(void *)&data);//only for schema gainga
  //GA1DArrayGenome<int> genome(*(data.geo_genome));
  //GA1DArrayGenome<int> pro_genome(*(data.pro_genome)); //only for schema gainga
  //genome.initializer(OptRun::GaInGa_GeoInitializer);	
  //genome.mutator(OptRun::GaInGa_GeoMutator);


  GA1DArrayGenome<int> genome(OptInf::NO_OF_GENE, OptRun::ObjectiveGALIB);
  genome.initializer(OptRun::Initializer);
  genome.mutator(OptRun::Mutator);

  //GA1DArrayGenome<int> genome(OptInf::NO_OF_GENE, OptRun::Ga_BFAObjective);
  //genome.initializer(OptRun::Ga_BFAInitializer);
  //genome.mutator(OptRun::Ga_BFAMutator);


  genome.comparator(GA1DArrayGenome<int>::ElementComparator);
  genome.crossover(GA1DArrayGenome<int>::OnePointCrossover);
  //genome.crossover(GA1DArrayGenome<int>::TwoPointCrossover);



  GASteadyStateGA ga(genome);
  //GASimpleGA ga(genome);



  GAParameterList params;
  GASteadyStateGA::registerDefaultParameters(params);
  //params.set(gaNpopulationSize, 1000);    // number of individuals in population
  //params.set(gaNpCrossover, 0.9);       // likelihood of doing crossover
  //params.set(gaNpMutation, 0.05);	// probability of mutation
  //params.set(gaNnGenerations, 200);	// number of generations
  params.set(gaNscoreFrequency, 200);	// how often to record scores
  params.set(gaNflushFrequency, 500);    // how often to flush scores to file
  params.set(gaNscoreFilename, "bogflushScores.dat");
  //params.parse(argc, argv, gaFalse);
  ga.parameters(params);




  ga.minimize();
  //ga.maximize();
  //ga.populationSize(1000);
  //ga.nGenerations(1000);

  ga.populationSize(OptInf::PopulationSize);
  ga.nGenerations(OptInf::GenerationSize);
  //ga.populationSize(1000);
  //ga.nGenerations(3000);


  ga.pMutation(0.05);
  ////ga.pCrossover(1);
  ga.pCrossover(0.95);
  ga.pConvergence(2-0.99);
  ga.nConvergence(1000);
  //ga.terminator(GAGeneticAlgorithm::TerminateUponConvergence);
  ga.selectScores(GAStatistics::AllScores);
  //ga.parameters(argc, argv, gaTrue);
  ga.terminator(GAGeneticAlgorithm::TerminateUponGeneration);
  //ga.terminator(GAGeneticAlgorithm::TerminateUponPopConvergence);
  //ga.terminator(GAGeneticAlgorithm::TerminateUponConvergence);
  //ga.terminator(GAGeneticAlgorithm::TerminateUponGeneration);
  //ga.terminator(GAGeneticAlgorithm::TerminateUponConvergence);

  ga.initialize();


  myfile<<"population size: "<<ga.populationSize()<<"\n";
  myfile<<"generation size: "<<ga.nGenerations()<<"\n";

  OptInf info;

  //OptRun::Ga_BFAInitializer(genome);
  //GASchema gaS = OptRun::GASchema::GA_BFA;
  //myfile<<"GASchema: GA_BFA"<<"\n";

  OptRun::Initializer(genome);
  GASchema gaS = OptRun::GASchema::OneGA;

  start = clock();

  //ga.terminator(GAGeneticAlgorithm::TerminateUponGeneration);
  //ga.evolve();


  myfile.close();

  int iG = 0;
  while (!ga.done()){

    genome = ga.statistics().bestIndividual();

    UpdateInfoObjective(info, genome );
    CallOptInformation(info, genome );



    //if(iG++%200 == 0)
    //{

    //  myfile.open(myfilename, ios::app);

    //  myfile << ga.generation()<<"-> " <<genome << "  \t-> "<<genome.score()<<"\n";  

    //  myfile << "N_of_Floor: " << OptInf::N_of_Floor << "    ( " << OptInf::LENGTH << " m  x " << OptInf::WIDTH << "m ) \n" << "load_e  "<< OptInf::load_e << "  \t load_m  "<< OptInf::load_m <<   "\n";

    //  myfile << "M_2: "<<OptInf::MyEdsec_o_MplRdsec <<"  \t "
    //    << "V_2: "<<OptInf::VEdsec_o_VplRdsec <<"  \t "
    //    << "Def_2: "<<OptInf::Deflectionsec_o_LengthSecBeam3o5 <<"  \t "
    //    << "M_1: "<<OptInf::MyEdpospri_o_MplRdpospri <<"  \t "
    //    << "V_1: "<< OptInf::VEdpri_o_VplRdpri<<"  \t"
    //    << "H_2: " << OptInf::CrossSection[genome.gene(4)][1] << "\t"
    //    << "H_1: " << OptInf::CrossSection[genome.gene(6)][1] << "\t"
    //    << "Penalty: " << OptInf::PenaltyTotal << "  \t\n";


    //  if (genome.gene(8) == 0)
    //  {
    //    myfile << "transverse\n";
    //  } 
    //  else
    //  {
    //    myfile << "longitudinal\n";
    //  }

    //  myfile << "2nd "<< genome.gene(4)<<": "<< OptInf::CrossSectionName[genome.gene(4)] << " \t 1st "<< genome.gene(6)<<": "<< OptInf::CrossSectionName[genome.gene(6)] << "\n";
    //  for(int i=0;i<OptInf::N_of_Floor;i++)
    //  {
    //    myfile << "( "<< genome.gene(11+i*4)<<":"<< OptInf::CrossSectionName[genome.gene(11+i*4)] << " - " << OptInf::CrossSection[genome.gene(11+i*4)][10] << "  &  " 
    //      << genome.gene(12+i*4) << " )  \t" << OptInf::FEdm_o_NbyRd_m[i] << "  \t"
    //      << "( "<< genome.gene(13+i*4)<<":"<< OptInf::CrossSectionName[genome.gene(13+i*4)] << " - " << OptInf::CrossSection[genome.gene(13+i*4)][10] << "  &  " 
    //      << genome.gene(14+i*4) << " )  \t" << OptInf::FEde_o_NbyRd_add_kyy_t_MEdy_o_MbyRd_e[i]  << " , " << OptInf::FEde_o_NbzRd_add_kzy_t_MEdy_o_MbyRd_e[i] << "\n";
    //  }
    //  myfile << "\n\n";
    //  myfile.close();

    //  resultfile.open(resultfilename);
    //  resultfile << ga.generation() << "\n";
    //  for (int i=0;i<OptInf::NO_OF_GENE;i++)
    //  {
    //    resultfile << genome.gene(i) << "\t";
    //  }
    //  resultfile.close();
    //}

    //cout << ga.statistics().convergence() << "\n";
    ++ga;
  }  


  //resultfile.open(resultfilename);
  //resultfile << -1 << "\n";
  for (int i=0;i<OptInf::NO_OF_GENE;i++)
  {
    //resultfile << genome.gene(i) << "\t";
    sPtr_bar->genes_string_[i] = genome.gene(i);
  }
  sPtr_bar->isOptFinished_ = true;
  //resultfile.close();

  //ga.flushScores();

  finish = clock();
  totaltime = (double)(finish-start)/CLOCKS_PER_SEC;


  resultfile.open(resultfilename);
  resultfile << ga.generation() << "\n";
    resultfile << "totaltime: " << totaltime << "\n";
    resultfile.close();

}
